﻿using System;
using System.Security;
using System.Security.Principal;
using System.Threading;

namespace AzManPermissions
{
  /// <summary>
  /// Allows AzMan operation checks against the active principal (see <see cref="T:System.Security.Principal.IPrincipal" />) using the language constructs defined for both declarative and imperative security actions. This class cannot be inherited.
  /// </summary>
  /// <remarks>
  /// Allows remote AzMan checks through connecting to an <see cref="T:AzManPermissions.IAzManService" /> WCF service which connects to an AzMan store. Using only one WCF service at a time.
  /// Connection information can be specified through AzManPermissions.IAzManService contract WCF endpoint binding in application configuration.
  /// </remarks>
  public sealed class AzManOperationRemotePermission : AzManBasePermission
  {
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="T:AzManPermissions.AzManOperationRemotePermission" /> class for the specified <paramref name="name" /> and <paramref name="operation" />.
    /// </summary>
    /// <param name="name">The name of the <see cref="T:System.Security.Principal.IPrincipal" /> object's user.</param>
    /// <param name="operation">The AzMan operation number to check for.</param>
    public AzManOperationRemotePermission(string name, int operation)
      : base(name, operation)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="T:AzManPermissions.AzManOperationRemotePermission" /> class for the specified <paramref name="name" /> and <paramref name="operation" /> and <paramref name="scopeName" />.
    /// </summary>
    /// <param name="name">The name of the <see cref="T:System.Security.Principal.IPrincipal" /> object's user.</param>
    /// <param name="operation">The AzMan operation number to check for.</param>
    /// <param name="scopeName">The name of a scope as defined in the AzMan store.</param>
    public AzManOperationRemotePermission(string name, int operation, string scopeName)
      : base(name, operation, scopeName)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="T:AzManPermissions.AzManOperationRemotePermission" /> class for the specified <paramref name="name" /> and <paramref name="operation" /> and <paramref name="scopeName" /> and <paramref name="objectName" />.
    /// </summary>
    /// <param name="name">The name of the <see cref="T:System.Security.Principal.IPrincipal" /> object's user.</param>
    /// <param name="operation">The AzMan operation number to check for.</param>
    /// <param name="scopeName">The name of a scope as defined in the AzMan store.</param>
    /// <param name="objectName">The string used for AzMan run-time auditing.</param>
    public AzManOperationRemotePermission(string name, int operation, string scopeName, string objectName)
      : base(name, operation, scopeName, objectName)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="T:AzManPermissions.AzManOperationRemotePermission" /> class with the specified <paramref name="descriptors" />.
    /// </summary>
    /// <param name="descriptors">The AzMan operation descriptors for the permission.</param>
    internal AzManOperationRemotePermission(PermissionDescriptor[] descriptors)
      : base(descriptors)
    {
    }

    #endregion Constructors

    #region Abstract overrides

    /// <summary>
    /// Initialization of the permission.
    /// </summary>
    internal override void Initialize()
    {
      //Principal and identity initialization
      IPrincipal currentPrincipal = Thread.CurrentPrincipal;
      identity = currentPrincipal.Identity;
      WindowsIdentity widentity = identity as WindowsIdentity;
      if (widentity == null)
      {
        throw new NotImplementedException(AzManPermissions.Properties.Resources.Security_OnlyWindowsAuthenticationImplemented);
      }
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="T:AzManPermissions.AzManOperationRemotePermission" /> class with the specified <paramref name="descriptors" />.
    /// </summary>
    /// <param name="descriptors">The AzMan operation descriptors for the permission.</param>
    /// <returns>The initialized instance of the <see cref="T:AzManPermissions.AzManOperationRemotePermission" /> class.</returns>
    internal override AzManBasePermission CreatePermission(PermissionDescriptor[] descriptors)
    {
      return new AzManOperationRemotePermission(descriptors);
    }

    /// <summary>
    /// Creates and returns an identical copy of the current permission.
    /// </summary>
    /// <returns>A copy of the current permission.</returns>
    public override IPermission Copy()
    {
      return new AzManOperationRemotePermission(descriptors);
    }

    /// <summary>
    /// Checks whether the current principal is allowed to perform the operation described in <paramref name="descriptor" />.
    /// </summary>
    /// <param name="descriptor">Description of the AzMan operation to check.</param>
    /// <returns>True, if the <see cref="T:System.Security.Principal.IPrincipal" /> object's user is allowed to perform the described operation.</returns>
    internal override bool AccessCheck(PermissionDescriptor descriptor)
    {
      if (identity.IsAuthenticated && (descriptor.name == null || string.Compare(identity.Name, descriptor.name, StringComparison.OrdinalIgnoreCase) == 0))
      {
        using (AzManServiceClient azManClient = new AzManServiceClient())
        {
          return azManClient.AccessCheck(descriptor.name, descriptor.objectName, descriptor.scopeName, descriptor.operation);
        }
      }
      return false;
    }

    #endregion Abstract overrides

    #region Internal and private helper methods and properties

    //Identity of the logged in user
    private IIdentity identity;

    #endregion Internal and private helper methods and properties
  }
}